home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / wais / waisgate / HTDaemonDIR.c < prev    next >
C/C++ Source or Header  |  1995-05-09  |  29KB  |  1,061 lines

  1. /*        TCP/IP based server for HyperText        HTDaemon.c
  2. **        ---------------------------------
  3. **
  4. **
  5. ** Compilation options:
  6. **    RULES        If defined, use rule file and translation table
  7. **    DIR_OPTIONS    If defined, -d options control directory access
  8. **
  9. **  Authors:
  10. **    TBL    Tim Berners-Lee, CERN
  11. **    JFG    Jean-Francois Groff, CERN
  12. **    JS    Jonathan Streets, FNAL
  13. **
  14. **  History:
  15. **       Sep 91  TBL     Made from earlier daemon files. (TBL)
  16. **    26 Feb 92  JFG    Bug fixes for Multinet.
  17. **       8 Jun 92  TBL  Bug fix: Perform multiple reads in case we don't get
  18. **                      the whole command first read.
  19. **    25 Jun 92  JFG  Added DECNET option through TCP socket emulation.
  20. **     6 Jan 93  TBL  Plusses turned to spaces between keywords
  21. **     7 Jan 93  JS   Bug fix: addrlen had not been set for accept() call
  22. **            Logging in GMT to file-YYMM in name
  23. */
  24. /* (c) CERN WorldWideWeb project 1990-1992. See Copyright.html for details */
  25.  
  26.  
  27. /*    Module parameters:
  28. **    -----------------
  29. **
  30. **  These may be undefined and redefined by syspec.h
  31. */
  32.  
  33. #define LISTEN_BACKLOG 2    /* Number of pending connect requests (TCP)*/
  34. #define MAX_CHANNELS 20        /* Number of channels we will keep open */
  35. #define WILDCARD '*'            /* Wildcard used in addressing */
  36. #define FIRST_TCP_PORT    5000    /* When using dynamic allocation */
  37. #define LAST_TCP_PORT 5999
  38.  
  39. #define MAX_LINE 512        /* HTTP request field line */
  40.  
  41. #ifndef RULE_FILE
  42. #define RULE_FILE        "/usr/local/etc/httpd.conf"
  43. #endif
  44.  
  45. #ifndef DEFAULT_EXPORT
  46. #define DEFAULT_EXPORT        "/Public"
  47. #endif
  48.  
  49. #include "HTUtils.h"
  50. #include "tcp.h"        /* The whole mess of include files */
  51. #include <time.h>
  52. #include "HTTCP.h"        /* Some utilities for TCP */
  53. #include "HTFormat.h"
  54. #include "HTInit.h"
  55.  
  56. #ifdef RULES            /* Use rules? */
  57. #include "HTRules.h"
  58. #endif
  59.  
  60. #include "HTFile.h"
  61. #include "HTParse.h"
  62.  
  63. extern int HTRetrieve PARAMS((char * arg, int soc));    /* Handle one request */
  64.  
  65.  
  66.  
  67. /*    Module-Global Variables
  68. **    -----------------------
  69. */
  70. PRIVATE enum role_enum {master, slave, transient, passive} role;
  71. PRIVATE SockA    soc_address;
  72. PRIVATE int    soc_addrlen;
  73. PRIVATE int    master_soc;    /* inet socket number to listen on */
  74. PRIVATE int    com_soc;    /* inet socket number to read on */
  75. #ifdef SELECT
  76. PRIVATE fd_set    open_sockets;    /* Mask of channels which are active */
  77. PRIVATE int    num_sockets;    /* Number of sockets to scan */
  78. #endif
  79. PRIVATE BOOLEAN    dynamic_allocation;    /* Search for port? */
  80.  
  81. PRIVATE time_t theTime;     /* A long integer giving the datetime in secs */
  82. PRIVATE struct tm * gmt;    /* The time in GMT broken down */
  83.  
  84. /*    Program-Global Variables
  85. **    ------------------------
  86. */
  87.  
  88. /* PUBLIC int    WWW_TraceFlag;    in libwww/HTString.c  Control flag for diagnostics */
  89.  
  90. PUBLIC char * log_file_name = 0;/* Log file name if any (WAIS code) */
  91.  
  92. PUBLIC char * HTClientProtocol = 0;    /* Protocol and version number */
  93. PUBLIC char * HTServerProtocol = "HTTP/1.0";
  94.  
  95. extern char * HTClientHost;    /* in library or HTRetrieve */
  96.  
  97. PRIVATE FILE * logfile;     /* Log file if any -- don't set up public one */
  98.  
  99. #define SPACE(c) ((c==' ')||(c=='\t')||(c=='\n')||(c=='\r'))     /*  DMX */
  100.  
  101.  
  102.  
  103. /*    Split fields
  104. **    ------------
  105. **
  106. ** On entry,
  107. **    s    points to string with words or quoted strings as fields
  108. **        separated by white space
  109. ** On exit,
  110. **    Return value points to first char of second word or NULL if none.
  111. **    First word is null-terminated.
  112. **    All trailing white space is overwritten with zero.
  113. */
  114. PRIVATE char * next_field ARGS1(char *, s)
  115. {
  116.     while(*s && SPACE(*s))s++;    /* skip leading blanks */
  117.     switch(*s) {
  118.         case '"':
  119.     case '\'':
  120.         s = strchr(s, *s);    /* skip quoted word */
  121.         if (!s) return s;    /* No closing quote! */
  122.         s++;            /* Skip closing quote */
  123.         break;
  124.     default:
  125.         while(*s && !SPACE(*s)) s++;    /* skip word */
  126.     }
  127.     if (!*s) return (char*)0;    /* Only one  or no word */
  128.     
  129.     *s++ = (char)0;        /* terminate first word */
  130.     while(SPACE(*s))s++;    /* skip leading blanks */
  131.     if (!*s) return (char*)0;    /* No second word */
  132.     return s;
  133. }
  134.  
  135.  
  136. /*    Send a string down a socket
  137. **    ---------------------------
  138. **
  139. **    The trailing zero is not sent.
  140. **    There is a maximum string length.
  141. */
  142. PUBLIC int HTWriteASCII ARGS2(int, soc, char *, s)
  143. {
  144. #ifdef NOT_ASCII
  145.     char * p;
  146.     char ascii[255];
  147.     char *q = ascii;
  148.     for (p=s; *p; p++) {
  149.         *q++ = TOASCII(*p);
  150.     }
  151.     return NETWRITE(soc, ascii, p-s);
  152. #else
  153.     return NETWRITE(soc, s, strlen(s));
  154. #endif
  155. }
  156.  
  157.  
  158.  
  159. /*                MIME OUTPUT
  160. **                ===========
  161. */
  162.  
  163. struct _HTStream {
  164.     HTStreamClass *        isa;
  165.     HTStreamClass        class;        /* special per object */
  166. };
  167.  
  168. /*    Write header for given content type
  169. **    -----------------------------------
  170. **
  171. **
  172. ** Note:  Under old protocol, non-plaintext files are sent untouched
  173. **    to work with multimedia hack in XMosaic.
  174. */
  175. #ifdef NOT_NEEDED_IT_SEEMS
  176. PUBLIC void HTSendHeader ARGS2(
  177.     int,                 soc, 
  178.     HTFormat,            rep)
  179. {
  180.     if (TRACE) fprintf(stderr, "HTDaemon: Retrieve Ok, Content-type %s\n",
  181.             HTAtom_name(rep));
  182.     if (HTClientProtocol) {
  183.     char buffer[4096];    /* @@ */
  184.     sprintf(buffer, "%s%s%s%s%s",
  185.         HTServerProtocol,
  186.           " 200 Document follows\r\nMIME-Version: 1.0\r\n",
  187.         "Content-Type: ", HTAtom_name(rep), "\r\n\r\n");
  188.     HTWriteASCII(soc, buffer);
  189.     
  190.     } else {            /* Old protocol */
  191.         if (rep == WWW_PLAINTEXT) {
  192.         HTWriteASCII(soc, "<PLAINTEXT>\r\n");
  193.     }
  194.     }
  195. }
  196. #endif
  197.  
  198. /*            Catch error messages
  199. **            --------------------
  200. **
  201. **    This shouldn't be necessary most of the time as HTLoadError
  202. **    will be called.
  203. **
  204. **    These entry points suppress the loading of HTAlert from the WWW library.
  205. **    These could be cleaned up and made very useful, esp
  206. **    remote progress reporting...
  207. */
  208.  
  209. PUBLIC void HTAlert ARGS1(CONST char *, Msg)
  210. {
  211.     fprintf(stderr, "500   Server reports:  %s\r\n", Msg);
  212. }
  213.  
  214.  
  215. PUBLIC void HTProgress ARGS1(CONST char *, Msg)
  216. {
  217.     /* fprintf(stderr, "   %s ...\n", Msg); */
  218. }
  219.  
  220.  
  221. PUBLIC BOOL HTConfirm ARGS1(CONST char *, Msg)
  222. {
  223.     return(NO);
  224. }
  225.  
  226. /*    Prompt for answer and get text back
  227. */
  228. PUBLIC char * HTPrompt ARGS2(CONST char *, Msg, CONST char *, deflt)
  229. {
  230.     char * rep = 0;
  231.     StrAllocCopy(rep, deflt);
  232.     return rep;
  233. }
  234.  
  235. /*    Write Error Message
  236. **    -------------------
  237. **
  238. **
  239. */
  240. PUBLIC int HTLoadError ARGS3(
  241.     HTStream*,             sink, 
  242.     int,                number,
  243.     CONST char *,            message)
  244. {
  245.     char buffer[4096];    /* @@ */
  246.     if (TRACE) fprintf(stderr, "HTDaemon: *** Returning ERROR %d:\n   %s\n",
  247.             number, message);
  248.     if (HTClientProtocol) {
  249.     sprintf(buffer,
  250.     "%s %d %s\r\nMIME-Version: 1.0\r\nContent-Type: text/HTML\r\n\r\n",
  251.         HTServerProtocol,
  252.         number,
  253.         message);
  254.     }
  255.     sprintf(buffer,
  256.      "<BODY><H1>Error %d</H1>\r\n\r\n  %s</BODY>\r\n",
  257.         number,
  258.         message);
  259.     (*sink->isa->put_string)(sink, buffer);
  260.     (*sink->isa->end_document)(sink);
  261.     (*sink->isa->free)(sink);
  262.     return HT_LOADED;
  263. }
  264.  
  265.  
  266. /*    Converter for writing the MIME wrapper to a document
  267. **    ----------------------------------------------------
  268. **
  269. **    Thsi kinda cheats because it returns the sink stream
  270. **    having first sent the wrapper down it.  Efficient -- I like it
  271. **    better than the MIME parser which gets in the way from then on.
  272. */
  273. PUBLIC HTStream * HTMIMEWrapper ARGS3(
  274.             HTPresentation *,    pres,
  275.             HTParentAnchor *,    anchcor,
  276.             HTStream*,        sink)
  277. {
  278.  
  279.     if (TRACE) fprintf(stderr, "HTDaemon: Retrieve Ok, Content-type %s\n",
  280.             HTAtom_name(pres->rep));
  281.     if (HTClientProtocol) {
  282.     char buffer[4096];    /* @@ */
  283.     sprintf(buffer, "%s%s%s%s%s",
  284.         HTServerProtocol,
  285.           " 200 Document follows\r\nMIME-Version: 1.0\r\n",
  286.         "Content-Type: ", HTAtom_name(pres->rep), "\r\n\r\n");
  287.     (*sink->isa->put_string)(sink, buffer);
  288.     
  289.     } else {            /* Old protocol */
  290.         if (pres->rep == WWW_PLAINTEXT) {
  291.         (*sink->isa->put_string)(sink, "<PLAINTEXT>\r\n");
  292.     }
  293.     }
  294.  
  295.     return sink;
  296. }
  297.  
  298.  
  299. /*____________________________________________________________________
  300. **
  301. **            Networking code
  302. */
  303.  
  304. /*        Bind to a TCP port
  305. **        ------------------
  306. **
  307. ** On entry,
  308. **    tsap    is a string explaining where to take data from.
  309. **        ""     means data is taken from stdin.
  310. **        "*:1729" means "listen to anyone on port 1729"
  311. **
  312. ** On exit,
  313. **    returns        Negative value if error.
  314. */
  315. int do_bind ARGS1(CONST char *, tsap)
  316. {
  317. #ifdef SELECT
  318.     FD_ZERO(&open_sockets);    /* Clear our record of open sockets */
  319.     num_sockets = 0;
  320. #endif
  321.  
  322. /*  Deal with PASSIVE socket:
  323. **
  324. **    A passive TSAP is one which has been created by the inet daemon.
  325. **    It is indicated by a void TSAP name.  In this case, the inet
  326. **    daemon has started this process and given it, as stdin, the connection
  327. **    which it is to use.
  328. */
  329.     if (*tsap == 0) {            /* void tsap => passive */
  330.  
  331.     dynamic_allocation = FALSE;        /* not dynamically allocated */
  332.     role = passive;    /* Passive: started by daemon */
  333.  
  334. #ifdef vms
  335.  
  336.     {   unsigned short channel;        /* VMS I/O channel */
  337.         struct string_descriptor {        /* This is NOT a proper descriptor*/
  338.             int size;            /*  but it will work.          */
  339.             char *ptr;            /* Should be word,byte,byte,long  */
  340.         } sys_input = {10, "SYS$INPUT:"};
  341.         int    status;            /* Returned status of assign */
  342.         extern int sys$assign();
  343.  
  344.         status = sys$assign(&sys_input, &channel, 0, 0);
  345.         com_soc = channel;    /* The channel is stdin */
  346.         CTRACE(tfp, "IP: Opened PASSIVE socket %d\n", channel);
  347.         return 1 - (status&1);
  348.     }    
  349. #else
  350.     com_soc = 0;        /* The channel is stdin */
  351.     CTRACE(tfp, "IP: PASSIVE socket 0 assumed from inet daemon\n");
  352.     return 0;        /* Good */
  353. #endif
  354.  
  355. /*  Parse the name (if not PASSIVE)
  356. */
  357.     } else {                /* Non-void TSAP */
  358.     char *p;        /* pointer to string */
  359.     char *q;
  360.     struct hostent  *phost;        /* Pointer to host - See netdb.h */
  361.     char buffer[256];        /* One we can play with */
  362.     register SockA * sin = &soc_address;
  363.  
  364.     strcpy(buffer, tsap);
  365.     p = buffer;
  366.  
  367. /*  Set up defaults:
  368. */
  369. #ifdef DECNET
  370.     sin->sdn_family = AF_DECnet;        /* Family = DECnet, host order  */
  371.     sin->sdn_objnum = 0;                /* Default: new object number, */
  372. #else  /* Internet */
  373.     sin->sin_family = AF_INET;        /* Family = internet, host order  */
  374.     sin->sin_port = 0;            /* Default: new port,    */
  375. #endif
  376.     dynamic_allocation = TRUE;        /*  dynamically allocated */
  377.     role = passive;             /*  by default */
  378.  
  379. /*  Check for special characters:
  380. */
  381.     if (*p == WILDCARD) {        /* Any node */
  382.         role = master;
  383.         p++;
  384.     }
  385.  
  386. /*  Strip off trailing port number if any:
  387. */
  388.     for(q=p; *q; q++)
  389.         if (*q==':') {
  390.             int status = 0;
  391.         *q++ = 0;        /* Terminate node string */
  392. #ifdef DECNET
  393.         sin->sdn_objnum = (unsigned char) HTCardinal(
  394.                         &status, &q, (unsigned int)65535);
  395. #else
  396.         sin->sin_port = htons((unsigned short)HTCardinal(
  397.                         &status, &q, (unsigned int)65535));
  398.         if (status<0) return status;
  399. #endif
  400.         if (*q) return -2;  /* Junk follows port number */
  401.         dynamic_allocation = FALSE;
  402.         break;        /* Exit from loop before we skip the zero */
  403.         } /*if*/
  404.  
  405. /* Get node name:
  406. */
  407. #ifdef DECNET  /* Empty address (don't care about the command) */
  408.     sin->sdn_add.a_addr[0] = 0;
  409.     sin->sdn_add.a_addr[1] = 0;
  410.     CTRACE(tfp, 
  411.         "Daemon: Parsed address as port %d, DECnet %d.%d\n",
  412.             (int) sin->sdn_objnum,
  413.             (int) sin->sdn_add.a_addr[0],
  414.             (int) sin->sdn_add.a_addr[1] ) ;
  415. #else
  416.     if (*p == 0) {
  417.         sin->sin_addr.s_addr = INADDR_ANY; /* Default: any address */
  418.  
  419.     } else if (*p>='0' && *p<='9') {   /* Numeric node address: */
  420.         sin->sin_addr.s_addr = inet_addr(p); /* See arpa/inet.h */
  421.  
  422.     } else {            /* Alphanumeric node name: */
  423.         phost=gethostbyname(p);    /* See netdb.h */
  424.         if (!phost) {
  425.         CTRACE(tfp, "IP: Can't find internet node name `%s'.\n",p);
  426.         return HTInetStatus("gethostbyname");  /* Fail? */
  427.         }
  428.         memcpy(&sin->sin_addr, phost->h_addr, phost->h_length);
  429.     }
  430.     CTRACE(tfp, 
  431.         "Daemon: Parsed address as port %d, inet %d.%d.%d.%d\n",
  432.             (int)ntohs(sin->sin_port),
  433.             (int)*((unsigned char *)(&sin->sin_addr)+0),
  434.             (int)*((unsigned char *)(&sin->sin_addr)+1),
  435.             (int)*((unsigned char *)(&sin->sin_addr)+2),
  436.             (int)*((unsigned char *)(&sin->sin_addr)+3));
  437. #endif
  438.     } /* scope of p */
  439.  
  440.  
  441. /*  Master socket for server:
  442. */
  443.     if (role == master) {
  444.  
  445. /*  Create internet socket
  446. */
  447. #ifdef DECNET
  448.     master_soc = socket(AF_DECnet, SOCK_STREAM, 0);
  449. #else
  450.     master_soc = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  451. #endif
  452.     if (master_soc<0)
  453.         return HTInetStatus("socket");
  454.       
  455.     CTRACE(tfp, "IP: Opened socket number %d\n", master_soc);
  456.     
  457. /*  If the port number was not specified, then we search for a free one.
  458. */
  459. #ifndef DECNET  /* irrelevant: no inetd */
  460.     if (dynamic_allocation) {
  461.         unsigned short try;
  462.         for (try=FIRST_TCP_PORT; try<=LAST_TCP_PORT; try++) { 
  463.         soc_address.sin_port = htons(try);
  464.         if (bind(master_soc,
  465.             (struct sockaddr*)&soc_address,
  466.                 /* Cast to generic sockaddr */
  467.             sizeof(soc_address)) == 0)
  468.             break;
  469.         if (try == LAST_TCP_PORT)
  470.             return HTInetStatus("bind");
  471.         }
  472.         CTRACE(tfp, "IP:  Bound to port %d.\n",
  473.             ntohs(soc_address.sin_port));
  474.     } else
  475. #endif
  476.       {                    /* Port was specified */
  477.         if (bind(master_soc,
  478.              (struct sockaddr*)&soc_address,    /* Cast to generic address */
  479.              sizeof(soc_address))<0)
  480.         return HTInetStatus("bind");
  481.     }
  482.     if (listen(master_soc, LISTEN_BACKLOG)<0)
  483.         return HTInetStatus("listen");
  484.  
  485.     CTRACE(tfp, "Daemon: Master socket(), bind() and listen() all OK\n");
  486. #ifdef SELECT
  487.     FD_SET(master_soc, &open_sockets);
  488.     if ((master_soc+1) > num_sockets) num_sockets=master_soc+1;
  489. #endif
  490.     return master_soc;
  491.     } /* if master */
  492.     
  493.     return -1;        /* unimplemented role */
  494.  
  495. } /* do_bind */
  496.  
  497.  
  498. /*    Read One Line
  499. **    -------------
  500. **
  501. ** On exit,
  502. **    returns     A malloced buffer with the line in.
  503. **            Rest of buffer is intact in command
  504. **            not converted into ASCII in case it is binary.
  505. */
  506. #define COMMAND_SIZE     2048   /* @@@@ WAIS queries can be big! */
  507. #define MINIMUM_READ      256   /* Minimum  left before we reallocate */
  508. #define ALLOCATION_UNIT  2048   /* Amount extra we add on each time */
  509.  
  510. PRIVATE int allocated = 0;
  511. PRIVATE int write_pointer = 0;
  512. PRIVATE int read_pointer = 0;
  513. PRIVATE char * command = NULL;
  514.  
  515. PRIVATE char * get_line ARGS1(
  516.     int,        soc)
  517.  
  518. {
  519.   char * line;
  520.   int status;
  521.   if (!command) {        /* First allocation */
  522.     allocated = COMMAND_SIZE;
  523.     write_pointer = 0;        /* valid data in buffer */
  524.     read_pointer = 0;
  525.     command = (char *)malloc(allocated+1);
  526.     *command = 0;      /* terminate "left-over" */
  527.     if (!command) {
  528.       fprintf(stderr, "Daemon: insufficient memory!\n");
  529.       exit(-5);
  530.     }
  531.   }
  532.  
  533.   for(;;) {    /* Get more if needed to complete line */
  534.     if (read_pointer == write_pointer) {    /* Need more data */
  535.       if (allocated - write_pointer < MINIMUM_READ) {
  536.     allocated = allocated + ALLOCATION_UNIT;
  537.     command = (char *)realloc(command, allocated+1);
  538.     if (!command) {
  539.       fprintf(stderr, "Daemon: No memory to reallocate command buffer!!\n");
  540.       exit(-6);
  541.     }
  542.       }
  543.       status = NETREAD(soc, command + write_pointer, allocated - write_pointer);
  544.       if (TRACE) fprintf(stderr,
  545.         "Daemon: net read returned %d, errno=%d\n", status, errno);
  546.       if (status<=0) {
  547.     free(command);
  548.     return NULL;    /* EOF or error before NL  */
  549.       }
  550.       write_pointer = write_pointer + status;
  551.       command[write_pointer] = 0;    /* terminate new string */
  552.     }
  553.  
  554.     /*    Find a line feed if there is one */
  555.     
  556.     for(; read_pointer < write_pointer; read_pointer++) {
  557.         char c;
  558. #ifdef NOT_ASCII
  559.     command[read_pointer] = TOASCII(command[read_pointer]);
  560. #endif
  561.     c = command[read_pointer];
  562.     if (!c) {
  563.       free(command);
  564.       return NULL;   /* Panic! read a 0! */
  565.     }
  566.     if (c=='\n') {   /* found a line feed: split buffers*/
  567.       line = command;
  568.       command[read_pointer++] = 0;     /* terminate and split lines */
  569.       write_pointer = write_pointer - read_pointer;
  570.       command = (char *)malloc(allocated+1);
  571.       if (!command) {
  572.         fprintf(stderr, "Daemon: insufficient memory for line!\n");
  573.         exit(-7);
  574.       }
  575.       memcpy(command, &line[read_pointer], write_pointer);  
  576.       read_pointer = 0;
  577.       return line;
  578.     }
  579.     
  580.     }  /* scan over buffer */
  581.   } /* end loop getting and scanning data */
  582. }
  583.  
  584.  
  585. /*    Handle one message
  586. **    ------------------
  587. **
  588. ** On entry,
  589. **    soc        A file descriptor for input and output.
  590. ** On exit,
  591. **    returns        >0    Channel is still open.
  592. **            0    End of file was found, please close file
  593. **            <0    Error found, please close file.
  594. */
  595. PUBLIC int HTHandle ARGS1(int, soc)
  596. {
  597.  
  598.   char *line1;         /* To hold command read from client */
  599.   char *keywords;    /* pointer to keywords in address */
  600.   int    status;
  601.   char * arg;        /* Pointer to the argument string */
  602.     
  603.        
  604.   if (command) {
  605.     free(command);
  606.     command = 0;
  607.   }
  608.  
  609.   line1 = get_line(soc);   /* Free me later */
  610.  
  611. /*    Log the call:
  612. */
  613.   if (logfile) {
  614.     fprintf(logfile, "%24.24s %s %s\n",
  615.           asctime(gmt), HTClientHost, line1);
  616.     fflush(logfile);    /* Actually update it on disk */
  617.     if (TRACE) fprintf(stderr, "Log: %24.24s %s %s",
  618.               asctime(gmt), HTClientHost, line1);
  619.   }
  620.  
  621.   arg=next_field(line1);
  622.         
  623. /*    @@@@@@@@@ Clear out any conversions to "present" left from before !!
  624. **    for when running in a loop
  625. */
  626.     HTFormatInit();    /* set up the list */
  627.  
  628. #ifndef NOCONVERT
  629.   HTSetConversion("text/plain", "www/present", HTMIMEWrapper,
  630.           1.0, 0.0, 0.0);
  631.   HTSetConversion("text/html", "www/present", HTMIMEWrapper,
  632.           1.0, 0.0, 0.0);
  633.   HTSetConversion("application/binary", "www/present", HTMIMEWrapper,
  634.           0.7, 0.0, 0.0);    /* Risky & more trouble */
  635. #endif            
  636.   if (arg) {
  637.  
  638.     HTClientProtocol = next_field(arg);
  639.     
  640.     if (HTClientProtocol) {
  641.       
  642.       char *line;     /* free me! @@@@@ */
  643.       char *q;
  644.       enum _request_field { INVALID, ACCEPT } field = INVALID;
  645.  
  646.       (void) next_field(HTClientProtocol); /* Strip trailing space */
  647.       while((line=get_line(soc)) != 0) {
  648.     char * p;
  649.     if (!*line) break;        /* Just LF */
  650.     p = line + strlen(line) - 1;
  651.     if (*p == '\r') *p = 0;
  652.     if (!*line) break;        /* Just CRLF */
  653.     if (!WHITE(*line)) {            /* has field */
  654.       p = strchr(line, ':');
  655.       if (!p) break;        /* Bad format -- junk line */
  656.       field = !strncasecmp(line, "Accept:", 7) ? ACCEPT
  657.         : INVALID;
  658.           p++;     /* skip colon */
  659.     } else {                        /* continuation line */
  660.       p = line;
  661.     }
  662.     if (field != INVALID) { /* Look for good one */
  663.       float quality = 1.0;
  664.       float maxbytes = 0.0;
  665.       float maxsecs = 0.0;
  666.     
  667.       q = next_field(p);
  668.       p = HTStrip(p);    /* Strip leading and trailing */
  669.     
  670.       while (q) {        /* more data left */
  671.         float value;
  672.         char * next;
  673.         char *equals = strchr(q, '=');
  674.         if (!equals) break;    /* bad syntax -- forget it! */
  675.         *equals++ = 0;    /* Split at equals */
  676.         next = next_field(equals);
  677.         if (sscanf(equals, "%f", &value)) {
  678.           char * attrib = HTStrip(q);
  679.           if (!strcasecmp(attrib, "q")) quality = value;
  680.           else if (!strcasecmp(attrib, "mxb")) maxbytes = value;
  681.           else if (!strcasecmp(attrib, "mxs")) maxsecs = value;
  682.         }
  683.         q = next;
  684.       } /* scan attributes */
  685.       
  686.       if (field == ACCEPT) {
  687.         if(TRACE) fprintf(stderr, "Daemon: Client accepts %s\n", p);
  688. #ifndef NOCONVERT
  689.         HTSetConversion(p, "www/present", HTMIMEWrapper,
  690.                 quality, 0.0, 0.0);    /* @@@@@@@@@@@@ fix zeroes */
  691. #endif
  692.       }
  693.     } /* if valid line */
  694.     free(line);
  695.       } /* scan lines */
  696.       free(line);
  697.     } /* if protocol */
  698.   } /* if arg */
  699.     
  700.         
  701. /*    Handle command
  702. */
  703.   if (0==strcmp("GET", line1)) {    /* Get a document     */
  704.  
  705. #ifdef OLD_CODE
  706.     if (arg) StrAllocCopy(original, arg)
  707.     keywords=strchr(arg, '?');
  708.     if (keywords) {
  709.         *keywords++ = 0;        /* Chop keywords off */
  710.         if (!*keywords) keywords = NULL;
  711.         else {
  712.         StrAllocCopy(key, keywords);
  713.         char *p;
  714.         for (p=key; *p; p++) if (*p == '+') *p = ' ';
  715.         /* Plusses to spaces */
  716.         HTUnEscape(keywords);
  717.         }
  718.     }
  719. #endif    
  720.     status =  HTRetrieve(arg, soc);
  721.     free(line1);
  722.     return 0;        /* EOF -- please close socket */
  723.   }
  724.  
  725.   HTWriteASCII(soc, "599 Unrecognised method name: `");
  726.   HTWriteASCII(soc, line1);
  727.   HTWriteASCII(soc, "'.\r\n");
  728.     
  729.   if (TRACE) fprintf(stderr,
  730.         "HTDaemon: Unrecognised method `%s'\n", command);
  731.   free(line1);
  732.   return 0;        /* End of file - please close socket */
  733.  
  734.  
  735. } /* handle */
  736.     
  737.  
  738. /*      Handle incoming messages                server_loop()
  739. **    -------------------------
  740. **
  741. ** On entry:
  742. **
  743. **      timeout         -1 for infinite, 0 for poll, else in units of 10ms
  744. **
  745. ** On exit,
  746. **    returns        The status of the operation, <0 if failed.
  747. **            0    means end of file
  748. **
  749. */
  750. #ifdef __STDC__
  751. PRIVATE int server_loop(void)
  752. #else
  753. PRIVATE int server_loop()
  754. #endif
  755. {
  756.     int tcp_status;        /* <0 if error, in general */
  757.     int timeout = -1;        /* No timeout required but code exists */
  758.     for(;;) {
  759.  
  760. /*  If it's a master socket, then find a slave:
  761. */
  762.         if (role == master) {
  763. #ifdef SELECT
  764.         fd_set        read_chans;
  765.         fd_set        write_chans;
  766.         fd_set        except_chans;
  767.         int            nfound;        /* Number of ready channels */
  768.         struct timeval    max_wait;   /* timeout in form for select() */
  769.     
  770.         FD_ZERO(&write_chans);        /* Clear the write mask */
  771.         FD_ZERO(&except_chans);        /* Clear the exception mask */
  772.  
  773. /*  If timeout is required, the timeout structure is set up. Otherwise
  774. **  (timeout<0) a zero is passed instead of a pointer to the struct timeval.
  775. */
  776.         if (timeout>=0) {
  777.         max_wait.tv_sec = timeout/100;
  778.         max_wait.tv_usec = (timeout%100)*10000;
  779.         }
  780.     
  781.         for (com_soc=(-1); com_soc<0;) {    /* Loop while connections keep coming */
  782.     
  783.         
  784. /*  The read mask expresses interest in the master channel for incoming
  785. **  connections) or any slave channel (for incoming messages).
  786. */
  787.  
  788. /*  Wait for incoming connection or message
  789. */
  790.             read_chans = open_sockets;     /* Read on all active channels */
  791.         if (TRACE) printf(
  792. "Daemon: Waiting for connection or message. (Mask=%x hex, max=%x hex).\n", 
  793.              *(unsigned int *)(&read_chans),
  794.             (unsigned int)num_sockets);
  795.         nfound=select(num_sockets, &read_chans,
  796.             &write_chans, &except_chans,
  797.             timeout >= 0 ? &max_wait : 0);
  798.     
  799.         if (nfound<0) return HTInetStatus("select()");
  800.         if (nfound==0) return 0;    /* Timeout */
  801.  
  802. /*    We give priority to existing connected customers. When there are
  803. **    no outstanding commands from them, we look for new customers.
  804. */
  805. /*      If a message has arrived on one of the channels, take that channel:
  806. */
  807.         {
  808.             int i;
  809.             for(i=0; i<num_sockets; i++)
  810.             if (i != master_soc)
  811.                 if (FD_ISSET(i, &read_chans)) {
  812.                 if (TRACE) printf(
  813.                     "Message waiting on socket %d\n", i);
  814.                 com_soc = i;        /* Got one! */
  815.                 break;
  816.             }
  817.             if (com_soc>=0) break; /* Found input socket */
  818.             
  819.         } /* block */
  820.         
  821. /*  If an incoming connection has arrived, accept the new socket:
  822. */
  823.         if (FD_ISSET(master_soc, &read_chans)) {
  824.                 soc_addrlen = sizeof(soc_address); /* JS 930107 */
  825.             CTRACE(tfp, "Daemon: New incoming connection:\n");
  826.             tcp_status = accept(master_soc,
  827.                     (struct sockaddr *)&soc_address,
  828.                     &soc_addrlen);
  829.             if (tcp_status<0)
  830.                 return HTInetStatus("accept");
  831.             CTRACE(tfp, "Daemon: Accepted new socket %d\n",
  832.                 tcp_status);
  833.             FD_SET(tcp_status, &open_sockets);
  834.             if ((tcp_status+1) > num_sockets)
  835.                 num_sockets=tcp_status+1;
  836.     
  837.         } /* end if new connection */
  838.     
  839.     
  840.         } /* loop on event */
  841.     
  842. #else    /* SELECT not supported */
  843.     
  844. /*        if (com_soc<0)   No slaves: must accept */
  845.           SockA peer_soc;
  846.           int peer_len = sizeof (peer_soc);
  847.             CTRACE(tfp, 
  848.             "Daemon: Waiting for incoming connection...\n");
  849. #ifdef DECNET
  850.             tcp_status = accept(master_soc, &peer_soc, &peer_len);
  851. #else  /* For which machine is this ??? rsoc is undeclared, what's mdp ? */
  852.             tcp_status = accept(master_soc,
  853.                     &rsoc->mdp.soc_tcp.soc_address,
  854.                     &rsoc->mdp.soc_tcp.soc_addrlen);
  855. #endif
  856.             if (tcp_status<0)
  857.             return HTInetStatus("accept");
  858.             com_soc = tcp_status;    /* socket number */
  859.             CTRACE(tfp, "Daemon: Accepted socket %d\n", tcp_status);
  860. /*        }  end if no slaves */
  861.     
  862. #endif
  863.  
  864.     }  /* end if master */
  865.  
  866.  
  867. /* com_soc is now valid for read */
  868.  
  869.     {
  870.         SockA addr;
  871.         int namelen = sizeof(addr);
  872.         char ip_address[16];
  873. #ifdef DECNET
  874.         StrAllocCopy(HTClientHost, "DecnetClient");
  875.         /* TBD */
  876. #else
  877.         getpeername(com_soc, (struct sockaddr*)&addr, &namelen);
  878.         
  879.         strncpy(ip_address,
  880.              inet_ntoa(addr.sin_addr), sizeof(ip_address));
  881.         StrAllocCopy(HTClientHost, ip_address);
  882. #endif
  883.     }
  884.  
  885. /*  Read the message now on whatever channel there is:
  886. */
  887.         CTRACE(tfp,"Daemon: Reading socket %d from host %s\n",
  888.         com_soc, HTClientHost);
  889.  
  890.     tcp_status=HTHandle(com_soc);
  891.     
  892.     if(tcp_status<=0) {                /* EOF or error */
  893.         if (tcp_status<0) {                /* error */
  894.             CTRACE(tfp,
  895.         "Daemon: Error %d handling incoming message (errno=%d).\n",
  896.              tcp_status, errno);
  897.             /* DONT return HTInetStatus("netread");     error */
  898.         } else {
  899.         CTRACE(tfp, "Daemon: Socket %d disconnected by peer\n",
  900.             com_soc);
  901.             }
  902.         if (role==master) {
  903.         NETCLOSE(com_soc);
  904. #ifdef SELECT
  905.         FD_CLR(com_soc, &open_sockets);
  906. #endif
  907.         } else {  /* Not multiclient mode */
  908. #ifdef VM
  909.         return -69;
  910. #else
  911.         return -ECONNRESET;
  912. #endif
  913.         }
  914.     } else {/* end if handler left socket open */
  915.         NETCLOSE(com_soc);
  916. #ifdef SELECT
  917.         FD_CLR(com_soc, &open_sockets);
  918. #endif
  919.         }
  920.     }; /* for loop */
  921. /*NOTREACHED*/
  922. } /* end server_loop */
  923.  
  924.  
  925. /*        Main program
  926. **        ------------
  927. **
  928. **    Options:
  929. **    -v        verify: turn trace output on to stdout 
  930. **    -a addr        Use different tcp port number and style
  931. **    -p port        Prefered
  932. **    -l file        Log requests in ths file
  933. **    -r file        Take rules from this file
  934. **    -R file        Clear rules and take rules from file.
  935. **
  936. **    Parameters:
  937. **        directory    directory to export
  938. */
  939. int main ARGS2 (
  940.     int,    argc,
  941.     char**,    argv)
  942. {
  943.     int status;
  944. #ifdef RULES
  945.     int rulefiles = 0;        /* Count number loaded */
  946. #endif
  947.     char * addr = "";        /* default address */
  948.     char *directory = NULL;
  949.  
  950.     WWW_TraceFlag = 0;        /* diagnostics off by default */
  951. #ifdef RULES
  952.     HTClearRules();
  953. #endif
  954.     {
  955.         int a;
  956.         
  957.     for (a=1; a<argc; a++) {
  958.         
  959.         if (0==strcmp(argv[a], "-v")) {
  960.             WWW_TraceFlag = 1;
  961.  
  962.         } else if (0==strcmp(argv[a], "-a")) {
  963.             if (++a<argc) addr = argv[a];
  964.  
  965.         } else if (0==strcmp(argv[a], "-p")) {
  966.             if (++a<argc) {
  967.             addr = (char*)malloc(strlen(argv[a])+10);
  968.             sprintf(addr, "*:%s", argv[a]);
  969.         }
  970. #ifdef RULES
  971.         } else if (0==strcmp(argv[a], "-r")) {
  972.             if (++a<argc) { 
  973.             if (HTLoadRules(argv[a]) < 0) exit(-1);
  974.             rulefiles++;
  975.         }
  976.         } else if (0==strcmp(argv[a], "-R")) {
  977.         rulefiles++;        /* Inhibit rule file load */
  978. #endif
  979. #ifdef DIR_OPTIONS
  980.         } else if (0==strncmp(argv[a], "-d", 2)) {
  981.             char *p = argv[a]+2;
  982.         for(;*p;p++) {
  983.             switch (argv[a][2]) {
  984.             case 'b':    HTDirReadme = HT_DIR_README_BOTTOM; break;
  985.             case 'n':    HTDirAccess = HT_DIR_FORBID; break;
  986.             case 'r':    HTDirReadme = HT_DIR_README_NONE; break;
  987.             case 's':   HTDirAccess = HT_DIR_SELECTIVE; break;
  988.             case 't':    HTDirReadme = HT_DIR_README_TOP; break;
  989.             case 'y':    HTDirAccess = HT_DIR_OK; break;
  990.             default:
  991.             fprintf(stderr, 
  992.                "HTDaemon: bad -d option %s\n", argv[a]);
  993.             exit(-4);
  994.             }
  995.         } /* loop over characters */
  996. #endif
  997.  
  998.         } else if (0==strcmp(argv[a], "-l")) { /* template */
  999.             if (++a<argc) {
  1000.             time(&theTime);
  1001.             gmt = gmtime(&theTime);
  1002.             log_file_name = malloc(strlen(argv[a]) + 5 + 1);
  1003.             sprintf(log_file_name,
  1004.                 argv[a], (gmt->tm_year) %100 , gmt->tm_mon + 1);
  1005.             logfile = fopen(log_file_name, "a");
  1006.         }
  1007.         if (!logfile) {
  1008.             fprintf(stderr,
  1009.             "Can't open log file %s\n", argv[a]);
  1010.             logfile = stderr;
  1011.         }
  1012.         } else if (argv[a][0] != '-') {    /* Parameter */
  1013.             if (!directory) directory = argv[a];
  1014.         
  1015.         } /*ifs */
  1016.     } /* for each arg */
  1017.     } /* scope of a */
  1018.  
  1019. #ifdef RULES
  1020.  
  1021.     if (rulefiles==0) {        /* No mention */
  1022.         if (!directory) {
  1023.         if (HTLoadRules(RULE_FILE) < 0) {    /* Default rule file? */
  1024.             directory = DEFAULT_EXPORT;
  1025.         };
  1026.     }
  1027.     if (directory) {
  1028.         char * mapto = malloc(strlen(directory)+5);
  1029.         sprintf(mapto, "file://%s%s/*", HTHostName(), directory);
  1030.         HTAddRule(HT_Pass, "/*", mapto);
  1031.         HTAddRule(HT_Fail, "*", NULL);
  1032.     }
  1033.     } else {
  1034.         if (directory) {
  1035.         fprintf(stderr,
  1036.             "Warning: -r or -R specified so %s directory param ignored\n",
  1037.             directory);
  1038.     }
  1039.     }
  1040. #endif
  1041.     
  1042.     
  1043.     status = do_bind(addr);
  1044.     if (status<0) {
  1045.         fprintf(stderr, "Daemon: Bad setup: Can't bind and listen on port.\n");
  1046.         fprintf(stderr, "     (Possibly server already running, for example).\n");
  1047.     exit(status);
  1048.     }
  1049.     
  1050.     status = server_loop();
  1051.  
  1052.     if (status<0) {
  1053.         /* printf("Error in server loop.\n");  not error if inetd-started*/
  1054.     exit(status);
  1055.     }
  1056.     
  1057.     exit(0);
  1058.     return 0;    /* NOTREACHED -- For gcc */
  1059. }
  1060.  
  1061.